package com.twitch.financialAnalysis.business.service;

import com.twitch.financialAnalysis.business.dao.*;
import com.twitch.financialAnalysis.business.pojo.constant.TableConstants;
import com.twitch.financialAnalysis.business.pojo.constant.YearConstants;
import com.twitch.financialAnalysis.business.pojo.dto.ConstructionInvestDTO;
import com.twitch.financialAnalysis.business.pojo.po.*;
import com.twitch.financialAnalysis.business.pojo.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

@Slf4j
@Service
public class SupportService {

    @Autowired
    private ConstructionInvestDao constructionInvestDao;

    @Autowired
    private LiquidityDao liquidityDao;

    @Autowired
    private FinancingDao financingDao;

    @Autowired
    private DepreciationDao depreciationDao;

    @Autowired
    private AmortizationDao amortizationDao;

    /**
     * ensure that there is no null value in the table
     *
     * @param object
     */
    public void checkNull(Object object) {
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                String name = fields[i].getName();
                name = name.substring(0, 1).toUpperCase() + name.substring(1); //将属性的首字符大写，方便构造get，set方法
                String type = fields[i].getGenericType().toString(); //获取属性的类型
                Method method = object.getClass().getMethod("get" + name);
                Object o = method.invoke(object);
                if (type.equals("class java.lang.Double") && o == null) {
                    Method setMethod = object.getClass().getMethod("set" + name, Double.class);
                    setMethod.invoke(object, TableConstants.DEFAULT_DOUBLE_EMPTY);
                }
                if (type.equals("class java.lang.Integer") && o == null) {
                    Method setMethod = object.getClass().getMethod("set" + name, Integer.class);
                    setMethod.invoke(object, TableConstants.DEFAULT_INTEGER_EMPTY);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * save table constructionInvest
     *
     * @param investVO
     * @return
     */
    public Integer saveConstructionInvest(ConstructionInvestVO investVO) {
        List<ConstructionInvestConfig> configList = investVO.getConfigs();
        BaseConstructionInvest invest = new BaseConstructionInvest();
        for (ConstructionInvestConfig constructionInvestConfig : configList) {
            //ensure table don't contains null
            checkNull(constructionInvestConfig);
            BeanUtils.copyProperties(constructionInvestConfig, invest);
            //1.1 software development and maintenance
            //first year
            if (constructionInvestConfig.getYear() == YearConstants.YEAR_FIRST) {
                invest.setPlatformAndApplicationCost(constructionInvestConfig.getDeveloperNum() * constructionInvestConfig.getLaborCost());
                invest.setSoftwareEnhanceCost(TableConstants.DEFAULT_DOUBLE_EMPTY);
            } else {
                //other year
                invest.setPlatformAndApplicationCost(TableConstants.DEFAULT_DOUBLE_EMPTY);
                invest.setSoftwareEnhanceCost(constructionInvestConfig.getDeveloperNum() * constructionInvestConfig.getLaborCost());
            }
            invest.setSoftwareCost(invest.getPlatformAndApplicationCost() + invest.getSoftwareEnhanceCost());
            //1.2 tools
            invest.setEquipmentAndToolCost(invest.getEquipmentAndTool());
            //1.3 other cost of construction
            invest.setProjectStartCost(invest.getProjectStart());
            invest.setConstructionManagementCost(invest.getConstructionManagement());
            invest.setConstructionOtherCost(invest.getConstructionManagementCost() + invest.getConstructionManagementCost());
            //1.4 base reverse fund cost
            invest.setBaseReserveFund(0.1 * invest.getSoftwareCost() * invest.getEquipmentAndToolCost() * invest.getConstructionManagementCost());
            invest.setBaseReserveFundCost(invest.getBaseReserveFund());
            //1 total construction investment
            invest.setConstructionInvest(invest.getSoftwareCost() + invest.getEquipmentAndToolCost() + invest.getConstructionManagementCost() + invest.getBaseReserveFundCost());
            //2 project invest total
            invest.setProjectConstructionCostTotal(invest.getConstructionInvest());

            if (constructionInvestConfig.getId() == null) {
                constructionInvestDao.insertSelective(invest);
            } else {
                constructionInvestDao.updateByPrimaryKeySelective(invest);
            }
        }
        return 1;
    }

    /**
     * get table constructionInvest
     *
     * @param searchVO
     * @return ConstructionInvestDTO
     */
    public ConstructionInvestDTO getConstructionInvest(BaseSearchVO searchVO) {
        Example example = new Example(BaseConstructionInvest.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", searchVO.getProjectId());
        List<BaseConstructionInvest> investList = constructionInvestDao.selectByExample(example);
        ConstructionInvestDTO constructionInvestDTO = new ConstructionInvestDTO();
        constructionInvestDTO.setInvestList(investList);
        return constructionInvestDTO;
    }

    /**
     * save table Liquidity
     *
     * @param liquidityVO
     * @return
     */
    public Integer saveLiquidity(LiquidityVO liquidityVO) {
        List<BaseLiquidity> liquidityList = liquidityVO.getLiquidityList();
        BaseLiquidity liquidity = new BaseLiquidity();
        for (int i = 0; i < liquidityList.size(); i++) {
            checkNull(liquidityList.get(i));
            BeanUtils.copyProperties(liquidityList.get(i), liquidity);
            //1 liquid asset
            liquidity.setLiquidAsset(liquidity.getCash() + liquidity.getReceivableAccount());
            //2 liquid liabilities
            liquidity.setLiquidLiabilities(liquidity.getAccountPayable());
            //3 liquidity
            liquidity.setLiquidity(liquidity.getLiquidAsset() - liquidity.getLiquidLiabilities());

            if (liquidity.getYear() == YearConstants.YEAR_FIRST) {
                liquidity.setLiquidityIncreaseYear(liquidity.getLiquidity());
            } else {
                liquidity.setLiquidityIncreaseYear(liquidity.getLiquidity() - liquidityList.get(i - 1).getLiquidity());
            }

            if (liquidity.getId() == null) {
                liquidityDao.insertSelective(liquidity);
            } else {
                liquidityDao.updateByPrimaryKeySelective(liquidity);
            }
        }
        return 1;
    }

    /**
     * get table Liquidity
     *
     * @param searchVO
     * @return
     */
    public List<BaseLiquidity> getLiquidity(BaseSearchVO searchVO) {
        Example example = new Example(BaseLiquidity.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", searchVO.getProjectId());
        return liquidityDao.selectByExample(example);
    }

    /**
     * save table financing
     *
     * @param financingVO
     * @return
     */
    public Integer saveFinancing(FinancingVO financingVO) {
        List<BaseFinancing> financingList = financingVO.getFinancingList();
        BaseFinancing baseFinancing = new BaseFinancing();

        List<BaseConstructionInvest> investList = financingDao.getConstructionInvestList(financingVO.getProjectId());
        List<BaseLiquidity> liquidityList = financingDao.getLiquidity(financingVO.getProjectId());
        // TODO 贷款余额来自还本付息表

        for (int i = 0; i < financingList.size(); i++) {
            checkNull(financingList.get(i));
            baseFinancing = financingList.get(i);
            ;
            baseFinancing.setConstructionInvest(investList.get(i).getProjectConstructionCostTotal());
            baseFinancing.setLiquidity(liquidityList.get(i).getLiquidityIncreaseYear());
            baseFinancing.setInvestTotal(baseFinancing.getConstructionInvest() + baseFinancing.getLiquidity());
            //2.1.1 capital
            if (baseFinancing.getYear() == YearConstants.YEAR_FIRST) {
                baseFinancing.setCapitalBalance(baseFinancing.getCapital());
            } else {
                baseFinancing.setCapitalBalance(baseFinancing.getCapital() + financingList.get(i - 1).getCapitalBalance());
            }
            //2.2
            // TODO 贷款余额来自还本付息表

            if (baseFinancing.getId() == null) {
                financingDao.insertSelective(baseFinancing);
            } else {
                financingDao.updateByPrimaryKeySelective(baseFinancing);
            }
        }
        return 1;
    }

    /**
     * get table financing
     *
     * @param searchVO
     * @return
     */
    public List<BaseFinancing> getFinancing(BaseSearchVO searchVO) {
        Example example = new Example(BaseFinancing.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", searchVO.getProjectId());
        return financingDao.selectByExample(example);
    }

    /**
     * save table depreciation
     *
     * @param depreciationVO
     * @return
     */
    public Integer saveDepreciation(DepreciationVO depreciationVO) {
        List<BaseDepreciation> depreciationList = depreciationVO.getDepreciationList();

        Example example = new Example(BaseDepreciation.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", depreciationVO.getProjectId());
        List<BaseConstructionInvest> CIList = constructionInvestDao.selectByExample(example);

        for (int i = 0; i < depreciationList.size(); i++) {
            BaseDepreciation depreciation = depreciationList.get(i);
            //depreciation rate
            depreciation.setDepreciationRate((1 - depreciation.getResidualsRate()) / depreciationList.size());
            //1.1 the improvement of fixed assets
            depreciation.setEquipmentAndToolCost(CIList.get(i).getEquipmentAndToolCost());
            //1.2 depreciation charge in the current year
            Double sum = 0.0;
            for (int j = 0; j < depreciation.getYear(); j++) {
                sum += depreciationList.get(j).getEquipmentAndToolCost();
            }
            depreciation.setDepreciationChargeYear(depreciation.getDepreciationRate() * sum);
            if (depreciation.getYear() == YearConstants.YEAR_FIRST) {
                depreciation.setFixedAssetNV(depreciation.getEquipmentAndToolCost() - depreciation.getDepreciationChargeYear());
            } else {
                depreciation.setFixedAssetNV(depreciationList.get(i).getFixedAssetNV() + depreciation.getEquipmentAndToolCost() -
                        depreciation.getDepreciationChargeYear());
            }
            //2 and 3
            depreciation.setDepreciationChargeYearTotal(depreciation.getDepreciationChargeYear());
            depreciation.setFixedAssetNVEnd(depreciation.getFixedAssetNVEnd());

            if (depreciation.getId() == null) {
                depreciationDao.insertSelective(depreciation);
            } else {
                depreciationDao.updateByPrimaryKeySelective(depreciation);
            }
        }
        return 1;
    }

    /**
     * get table depreciation
     *
     * @param searchVO
     * @return
     */
    public List<BaseDepreciation> getDepreciation(BaseSearchVO searchVO) {
        Example example = new Example(BaseDepreciation.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", searchVO.getProjectId());
        return depreciationDao.selectByExample(example);
    }

    /**
     * save table amortization
     *
     * @param amortizationVO
     * @return
     */
    public Integer saveAmortization(AmortizationVO amortizationVO) {
        List<BaseAmortization> amortizationList = amortizationVO.getAmortizationList();

        Example example = new Example(BaseDepreciation.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", amortizationVO.getProjectId());
        List<BaseConstructionInvest> CIList = constructionInvestDao.selectByExample(example);

        for (int i = 0; i < amortizationList.size(); i++) {
            BaseAmortization amortization = amortizationList.get(i);
            //amortization rate
            amortization.setAmortizationRate(1.0 / amortizationList.size());
            //1 platform
            amortization.setPUnfixedAssetOVYear(CIList.get(i).getPlatformAndApplicationCost());
            //2 software
            amortization.setSUnfixedAssetOVYear(CIList.get(i).getSoftwareEnhanceCost());
            //3 construction
            amortization.setCUnfixedAssetOVYear(CIList.get(i).getConstructionOtherCost());
            if (amortization.getYear() == YearConstants.YEAR_FIRST) {
                //1.2
                amortization.setPAmortizationChargeYear(TableConstants.DEFAULT_DOUBLE_EMPTY);
                amortization.setPUnfixedNAV(amortization.getPUnfixedAssetOVYear() -
                        amortization.getPAmortizationChargeYear());
                //2.2
                amortization.setSAmortizationChargeYear(TableConstants.DEFAULT_DOUBLE_EMPTY);
                amortization.setSUnfixedNAV(amortization.getSUnfixedAssetOVYear() -
                        amortization.getSAmortizationChargeYear());
                //3.2
                amortization.setCAmortizationChargeYear(TableConstants.DEFAULT_DOUBLE_EMPTY);
                amortization.setCUnfixedNAV(amortization.getCUnfixedAssetOVYear() -
                        amortization.getCAmortizationChargeYear());
            } else {
                Double pSum = 0.0d, sSum = 0.0d, cSum = 0.0d;
                for (int j = 0; j < amortization.getYear(); j++) {
                    pSum += amortizationList.get(j).getPUnfixedAssetOVYear();
                    sSum += amortizationList.get(j).getSUnfixedAssetOVYear();
                    cSum += amortizationList.get(j).getCUnfixedAssetOVYear();
                }
                //1.2 1.3
                amortization.setPAmortizationChargeYear(amortization.getAmortizationRate() * pSum);
                amortization.setPUnfixedNAV(amortizationList.get(i - 1).getPUnfixedNAV() + amortization.getPUnfixedAssetOVYear() -
                        amortization.getPAmortizationChargeYear());
                //2.2 2.3
                amortization.setSAmortizationChargeYear(amortization.getAmortizationRate() * sSum);
                amortization.setSUnfixedNAV(amortizationList.get(i - 1).getSUnfixedNAV() + amortization.getSUnfixedAssetOVYear() -
                        amortization.getSAmortizationChargeYear());
                //3.2 3.3
                amortization.setCAmortizationChargeYear(amortization.getAmortizationRate() * cSum);
                amortization.setCUnfixedNAV(amortizationList.get(i - 1).getCUnfixedNAV() + amortization.getCUnfixedAssetOVYear() -
                        amortization.getCAmortizationChargeYear());
            }
            //amortization charge and unfixed asset in one year
            amortization.setAmortizationChargeYearTotal(amortization.getPAmortizationChargeYear() +
                    amortization.getSAmortizationChargeYear() + amortization.getCAmortizationChargeYear());
            amortization.setUnfixedAssetNAVTotal(amortization.getPUnfixedNAV() + amortization.getSUnfixedNAV() +
                    amortization.getCUnfixedNAV());

            if (amortization.getId() == null) {
                amortizationDao.insertSelective(amortization);
            } else {
                amortizationDao.updateByPrimaryKeySelective(amortization);
            }
        }
        return 1;
    }

    /**
     * get table amortization
     *
     * @param searchVO
     * @return
     */
    public List<BaseAmortization> getAmortization(BaseSearchVO searchVO) {
        Example example = new Example(BaseAmortization.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", searchVO.getProjectId());
        return amortizationDao.selectByExample(example);
    }



}
